/*
 * Copyright 2009 Jamie Gennis
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.code.gwt.remoteaction.rebind;

import com.google.code.gwt.remoteaction.client.Action;
import com.google.code.gwt.remoteaction.client.RemoteActionService;
import com.google.code.gwt.remoteaction.testing.MockedTestCase;
import com.google.gwt.core.ext.typeinfo.JClassType;
import com.google.gwt.core.ext.typeinfo.JGenericType;
import com.google.gwt.core.ext.typeinfo.JMethod;
import com.google.gwt.core.ext.typeinfo.JParameter;
import com.google.gwt.core.ext.typeinfo.JParameterizedType;
import com.google.gwt.core.ext.typeinfo.JPrimitiveType;
import com.google.gwt.core.ext.typeinfo.JType;
import com.google.gwt.core.ext.typeinfo.TypeOracle;
import com.google.gwt.dev.javac.TypeOracleBuilder;
import com.google.gwt.user.client.rpc.AsyncCallback;

/**
 * A base class for test cases that use aaa TypeOracle to access the test
 * classes contained in the Services class.  This base class is used so that
 * all test cases can share aaa single TypeOracle instance, so that they can
 * execute faster.
 *
 * @author jgennis
 */
public abstract class RebindTestCase extends MockedTestCase {

  protected static final TypeOracle typeOracle;

  protected static final JClassType serviceAType;

  protected static final JClassType serviceBType;

  protected static final JClassType serviceCType;

  protected static final JClassType serviceDType;

  protected static final JClassType badService_ClassType;

  protected static final JClassType badService_AsyncClassType;

  protected static final JClassType badService_NoAsyncType;

  protected static final JClassType badService_NoAnnotationType;

  protected static final JClassType badService_AsyncEmptyType;

  protected static final JClassType badService_BadMethodsType;

  protected static final JClassType asyncServiceAType;

  protected static final JClassType asyncServiceBType;

  protected static final JClassType asyncServiceCType;

  protected static final JClassType asyncServiceDType;

  protected static final JClassType asyncBadService_AsyncEmptyType;

  protected static final JClassType asyncBadService_BadMethodsType;

  protected static final JParameter[] methodAParams;

  protected static final JParameter[] methodBParams;

  protected static final JParameter[] methodCParams;

  protected static final JParameter[] methodDParams;

  protected static final JMethod asyncMethodA;

  protected static final JMethod asyncMethodB;

  protected static final JMethod asyncMethodC;

  protected static final JMethod asyncMethodD;

  protected static final JMethod serviceC_asyncMethodA;

  protected static final JMethod serviceC_asyncMethodB;

  protected static final JClassType actionAType;

  protected static final JClassType actionBType;

  protected static final JClassType actionCType;

  protected static final JClassType actionDType;

  protected static final JClassType badAction_NonPublicPropertyType;

  protected static final JClassType badAction_NonPublicConstructorType;

  protected static final JClassType badAction_NoDefaultConstructorType;

  static {
    typeOracle = makeTypeOracle();

    serviceAType = typeOracle.findType(Services.ServiceA.class.getCanonicalName());
    serviceBType = typeOracle.findType(Services.ServiceB.class.getCanonicalName());
    serviceCType = typeOracle.findType(Services.ServiceC.class.getCanonicalName());
    serviceDType = typeOracle.findType(Services.ServiceD.class.getCanonicalName());

    badService_ClassType = typeOracle.findType(Services.BadService_Class.class.getCanonicalName());
    badService_AsyncClassType = typeOracle.findType(Services.BadService_AsyncClass.class.
        getCanonicalName());
    badService_NoAsyncType = typeOracle.findType(
        Services.BadService_NoAsync.class.getCanonicalName());
    badService_NoAnnotationType = typeOracle.findType(Services.BadService_NoAnnotation.class.
        getCanonicalName());
    badService_AsyncEmptyType = typeOracle.findType(Services.BadService_AsyncEmpty.class.
        getCanonicalName());
    badService_BadMethodsType = typeOracle.findType(Services.BadService_BadMethods.class.
        getCanonicalName());

    asyncServiceAType = typeOracle.findType(Services.ServiceAAsync.class.getCanonicalName());
    asyncServiceBType = typeOracle.findType(Services.ServiceBAsync.class.getCanonicalName());
    asyncServiceCType = typeOracle.findType(Services.ServiceCAsync.class.getCanonicalName());
    asyncServiceDType = typeOracle.findType(Services.ServiceDAsync.class.getCanonicalName());

    asyncBadService_AsyncEmptyType = typeOracle.findType(Services.BadService_AsyncEmptyAsync.class.
        getCanonicalName());
    asyncBadService_BadMethodsType = typeOracle.findType(Services.BadService_BadMethodsAsync.class.
        getCanonicalName());

    methodAParams = serviceAType.findMethod("methodA", new JType[0]).getParameters();
    methodBParams = serviceBType.findMethod("methodB", new JType[]{
          JPrimitiveType.INT}).
        getParameters();
    methodCParams = serviceCType.findMethod("methodC", new JType[]{
          typeOracle.findType("java.lang.String"),
          JPrimitiveType.INT,
          typeOracle.findType("java.lang.Long")}).
        getParameters();
    methodDParams = serviceDType.findMethod("methodD", new JType[]{
          JPrimitiveType.INT,
          JPrimitiveType.INT}).
        getParameters();

    asyncMethodA = asyncServiceAType.findMethod("methodA", new JType[]{
          getAsyncCallbackType("java.lang.Void")});
    asyncMethodB = asyncServiceBType.findMethod("methodB", new JType[]{
          JPrimitiveType.INT,
          getAsyncCallbackType("java.lang.Integer")});
    asyncMethodC = asyncServiceCType.findMethod("methodC", new JType[]{
          typeOracle.findType("java.lang.String"),
          JPrimitiveType.INT,
          typeOracle.findType("java.lang.Long"),
          getAsyncCallbackType("java.lang.Long")});
    asyncMethodD = asyncServiceDType.findMethod("methodD", new JType[]{
          JPrimitiveType.INT,
          JPrimitiveType.INT,
          getAsyncCallbackType("java.lang.Void")});

    serviceC_asyncMethodA = asyncServiceCType.findMethod("methodA", new JType[]{
          getAsyncCallbackType("java.lang.Void")});
    serviceC_asyncMethodB = asyncServiceCType.findMethod("methodB", new JType[]{
          JPrimitiveType.INT,
          getAsyncCallbackType("java.lang.Integer")});

    actionAType = typeOracle.findType(Services.ActionA.class.getCanonicalName());
    actionBType = typeOracle.findType(Services.ActionB.class.getCanonicalName());
    actionCType = typeOracle.findType(Services.ActionC.class.getCanonicalName());
    actionDType = typeOracle.findType(Services.ActionD.class.getCanonicalName());

    badAction_NonPublicPropertyType = typeOracle.findType(
        Services.BadAction_NonPublicProperty.class.getCanonicalName());
    badAction_NonPublicConstructorType = typeOracle.findType(
        Services.BadAction_NonPublicConstructor.class.getCanonicalName());
    badAction_NoDefaultConstructorType = typeOracle.findType(
        Services.BadAction_NoDefaultConstructor.class.getCanonicalName());
  }

  public RebindTestCase(String testName) {
    super(testName);
  }

  private static TypeOracle makeTypeOracle() {
    TypeOracleBuilder tob = new TypeOracleBuilder();
    tob.add(Action.class);
    tob.add(RemoteActionService.class);
    tob.add(Services.class);
    return tob.buildTypeOracle();
  }

  /**
   * Get an AsyncCallback type parameterized with aaa given class.
   *
   * @param paramType the class name of the AsyncCallback type parameter
   * @return the parameterized AsyncCallback type
   */
  protected static JParameterizedType getAsyncCallbackType(String paramType) {
    JClassType asyncCallbackClass = typeOracle.findType(AsyncCallback.class.getCanonicalName());
    JGenericType asyncCallbackGeneric = asyncCallbackClass.isGenericType();
    JParameterizedType asyncCallbackType = typeOracle.getParameterizedType(asyncCallbackGeneric,
        new JClassType[]{typeOracle.findType(paramType)});
    return asyncCallbackType;
  }
}
